﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Fusion
{
    /// <summary> Fusion framework Class Extensions </summary>
    public static class Extensions
    {
        #region Object

        // Instantiates a type
        public static object CreateInstance( this Type type )
        {
            return Activator.CreateInstance( type );
        }

        #endregion

        #region Vector2

        /// <summary> To Vector3 </summary>
        public static Vector3 V3( this Vector2 vector )
        { return new Vector3( vector.X, vector.Y, 0 ); }
        /// <summary> To Vector3 with Z </summary>
        public static Vector3 V3( this Vector2 vector, float z )
        { return new Vector3( vector.X, vector.Y, z ); }

        /// <summary> Returns Rectangle with Width,Height from this.X, this.Y </summary>
        public static Rectangle Rectangle( this Vector2 vector )
        { return new Rectangle( 0, 0, (int)vector.X, (int)vector.Y ); }

        /// <summary> Returns Angle from this vector </summary>
        public static float ToAngle( this Vector2 vect )
        {
            if( vect.Length() == 0 )
                return 0.0f;

            Vector2 n = Vector2.Normalize( vect );
            return (float)Math.Atan2( n.Y, n.X ) + MathHelper.PiOver2;
        }

        /// <summary> Sets properties from angle </summary>
        public static void FromAngle( this Vector2 vector, float angle )
        {
            vector.X = (float)Math.Sin( angle );
            vector.Y = (float)Math.Cos( angle ) * -1;
        }

		public static void Design(this Vector2 signed)
		{
			if (signed.X < 0)
				signed.X *= -1;
			if (signed.Y < 0)
				signed.Y *= -1;
		}

        public static Vector2 Unsigned( this Vector2 signed )
        {
			Vector2 ret = signed;
			if (ret.X < 0)
				ret.X *= -1;
			if (ret.Y < 0)
				ret.Y *= -1;

            return ret;
        }

        /// <summary> Sets length </summary>
        public static void Length( this Vector2 value, float length )
        {
            if( value.Length() == 0 )
                value = Vector2.UnitY;
            else
                value = Vector2.Normalize( value ) * length;
        }

		/// <summary> Returns the translation to a target vector </summary>
		public static Vector2 TranslationTo(this Vector2 value, Vector2 dest)
		{
			return dest - value;
		}
		
		/// <summary> Modulus </summary>
        public static Vector2 Mod( this Vector2 v, float mod )
        { return new Vector2( v.X % mod, v.Y % mod ); }
        /// <summary> Modulus </summary>
        public static Vector2 Mod( this Vector2 v, Vector2 mod )
        { return new Vector2( v.X % mod.X, v.Y % mod.Y ); }

        /// <summary> Snap to Grid </summary>
        public static Vector2 Snap( this Vector2 v, float mod )
        {
            return new Vector2( v.X -= v.X % mod, v.Y -= v.Y % mod );
        }
        /// <summary> Snap to Grid </summary>
        public static Vector2 Snap( this Vector2 v, Vector2 mod )
        {
            return new Vector2( v.X -= v.X % mod.X, v.Y -= v.Y % mod.Y );
        }

        /// <summary> Transforms a point by parameters </summary>
        /// <param name="point">The point to transform</param>
        /// <param name="translation">Translation to apply</param>
        /// <param name="scale">Scale to apply</param>
        /// <param name="angle">Rotation to apply</param>
        /// <returns>Transformed point</returns>
        public static Vector2 Transform( this Vector2 point, Vector2 translation, Vector2 scale, float angle )
        {
            return translation + ((point * scale).Rotate( angle ));
        }


        // Rotates a vector around zero
        public static Vector2 Rotate( this Vector2 vector, float angle )
        {
            if( angle == 0 )
                return vector;
            else
                return Vector2.Transform( vector, Matrix.CreateRotationZ( angle ) );
        }
        public static Vector2 Rotate( this Vector2 vector, Vector2 center, float angle )
        {
            if( angle == 0 )
                return vector;
            else
                return center + Vector2.Transform( vector - center, Matrix.CreateRotationZ( angle ) );
        }

        /// <summary> Looks at a point </summary>
        /// <param name="at">The point to look at</param>
        public static void Look( this Vector2 vector, Vector2 at )
        {
            vector = Vector2.Normalize( at - vector );
        }

        #endregion

        #region Vector3

        /// <summary> To Vector2 </summary>
        public static Vector2 V2( this Vector3 vector )
        { return new Vector2( vector.X, vector.Y ); }


        /// <summary> Set Length </summary>
        public static void Length( this Vector3 value, float length )
        {
            if( value.Length() == 0 )
                value = Vector3.Zero;
            else
                value = Vector3.Normalize( value ) * length;
        }

        public static Vector3 Unsigned( this Vector3 signed )
        {
            if( signed.X < 0 )
                signed.X *= -1;
            if( signed.Y < 0 )
                signed.Y *= -1;
            if( signed.Z < 0 )
                signed.Z *= -1;

            return signed;
        }

        /// <summary> Modulus </summary>
        public static Vector3 Mod( this Vector3 v, float mod )
        {
            return new Vector3( v.X % mod, v.Y % mod, v.Z % mod );
        }
        /// <summary> Modulus </summary>
        public static Vector3 Mod( this Vector3 v, Vector3 mod )
        {
            return new Vector3( v.X % mod.X, v.Y % mod.Y, v.Z % mod.Z );
        }

        /// <summary> Snap to Grid </summary>
        public static void Snap( Vector3 v, float mod )
        {
            v.X -= v.X % mod;
            v.Y -= v.Y % mod;
            v.Z -= v.Z % mod;
        }
        /// <summary> Snap to Grid </summary>
        public static void Snap( Vector3 v, Vector3 mod )
        {
            v.X -= v.X % mod.X;
            v.Y -= v.Y % mod.Y;
            v.Z -= v.Z % mod.Z;
        }

        /// <summary> Transforms a point by parameters </summary>
        /// <param name="point">The point to transform</param>
        /// <param name="translation">Translation to apply</param>
        /// <param name="scale">Scale to apply</param>
        /// <param name="angle">Rotation to apply</param>
        /// <returns>Transformed point</returns>
        public static Vector3 Transform( this Vector3 point, Vector3 translation, Vector3 scale, float angle )
        {
            return translation + (point * scale).Rotate( angle );
        }

        // Rotates a vector around Z
        public static Vector3 Rotate( this Vector3 vector, float angle )
        {
            if( angle == 0 )
                return vector;
            else
                return Vector3.Transform( vector, Matrix.CreateRotationZ( angle ) );
        }
        public static Vector3 Rotate( this Vector3 vector, float yaw, float pitch, float roll )
        {
#if SILVERLIGHT
			return Vector3.Zero; // SilverSprite doesn't seem to have Matrix.CreateFromYPR(), which sucks
#else
            if( yaw == 0 && pitch == 0 && roll == 0 )
                return vector;
            else
                return Vector3.Transform( vector, Matrix.CreateFromYawPitchRoll( yaw, pitch, roll ) );
#endif
        }
        public static Vector3 Rotate( this Vector3 vector, Vector3 center, float angle )
        {
            if( angle == 0 )
                return vector;
            else
                return center + Vector3.Transform( vector - center, Matrix.CreateRotationZ( angle ) );
        }

        /// <summary> Looks at a vector </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        public static void Look( this Vector3 vector, Vector3 at )
        {
            vector = Vector3.Normalize(at - vector);
        }

        #endregion

        public static void Normalize( this int value )
        {
            if( value < 0 )
                value = -1;
            else if( value > 0 )
                value = 1;
            else
                value = 0;
        }

        public static void Normalize( this float value )
        {
            if( value < 0 )
                value = -1;
            else if( value > 0 )
                value = 1;
            else
                value = 0;
        }

        #region Rectangle

        /// <summary> Creates rectangle from values </summary>
        public static Rectangle FromCorners( this Rectangle rect, Vector2 tl, Vector2 br )
        {
            rect.X = (int)tl.X;
            rect.Y = (int)tl.Y;
            rect.Width = (int)(br.X - tl.X);
            rect.Height = (int)(br.Y - tl.Y);
            return rect;
        }

        /// <summary> Convertes rectangle position to Vector2</summary>
        /// <returns>Vector2 initialized with the X,Y of the rectangle</returns>
        public static Vector2 Position( this Rectangle value )
        {
            return new Vector2( value.X, value.Y );
        }
        /// <summary> Convertes rectangle size to Vector2</summary>
        /// <returns>Vector2 initialized with the Width,Height of the rectangle</returns>
        public static Vector2 Size( this Rectangle value )
        {
            return new Vector2( value.Width, value.Height );
        }
        // Convert to Vector3
        public static Vector3 Position3( Rectangle value )
        {
            return new Vector3( value.X, value.Y, 0 );
        }
        public static Vector3 Size3( Rectangle value )
        {
            return new Vector3( value.Width, value.Height, 1 );
        }

        #endregion

    }
}
